﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using Legend.Fakes.Configuration;
using Legend.Fakes.Api;
using Legend.Fakes;

namespace Rhino.Mocks
{
    public static class RhinoExtensions
    {
        public static void AssertWasCalled<TFake>(this TFake fakedObject, Expression<Action<TFake>> memberAccessor) where TFake : class
        {
            Fake.Assert(fakedObject).WasCalled(memberAccessor);
        }

        public static void AssertWasCalled<TFake, TMember>(this TFake fakedObject, Expression<Func<TFake, TMember>> memberAccessor) where TFake : class
        {
            Fake.Assert(fakedObject).WasCalled(memberAccessor);
        }

        public static void AssertWasNotCalled<TFake>(this TFake fakedObject, Expression<Action<TFake>> memberAccessor) where TFake : class
        {
            Fake.Assert(fakedObject).WasNotCalled(memberAccessor);
        }

        public static void AssertWasNotCalled<TFake, TMember>(this TFake fakedObject, Expression<Func<TFake, TMember>> memberAccessor) where TFake : class
        {
            Fake.Assert(fakedObject).WasNotCalled(memberAccessor);
        }

        public static RhinoCompatibleConfiguration<TFake, object> Stub<TFake>(this TFake fakedObject, Expression<Action<TFake>> memberAccessor) where TFake : class
        {
            var configuration = new FakeConfiguration<TFake>(Fake.GetFakeObject(fakedObject));
            configuration.CallsTo(memberAccessor);

            return new RhinoCompatibleConfiguration<TFake, object>(configuration);
        }

        public static RhinoCompatibleConfiguration<TFake, TReturn> Stub<TFake, TReturn>(this TFake fakedObject, Expression<Func<TFake, TReturn>> memberAccessor) where TFake : class
        {
            var configuration = new FakeConfiguration<TFake>(Fake.GetFakeObject(fakedObject));
            configuration.CallsTo(memberAccessor);

            return new RhinoCompatibleConfiguration<TFake, TReturn>(configuration);
        }
    }

    public static class MockRepository
    {
        public static TFake GenerateMock<TFake>() where TFake : class 
        {
            return A.Fake<TFake>();
        }

        //public static TFake GenerateMock<TFake>(object[] argumentsForConstructor) where TFake : class 
        //{
        //    return A.Fake<TFake>(Expression.New(typeof(TFake).GetConstructor(argumentsForConstructor.Select(x => x.GetType()).ToArray()),
        //        argumentsForConstructor.Select(x => Expression.Constant(x)).ToArray()));
        //}

        public static TFake GenerateStub<TFake>() where TFake : class 
        {
            return A.Fake<TFake>();
        }
    }

    public class RhinoCompatibleConfiguration<TFake, TReturn> where TFake : class
    {
        private FakeConfiguration<TFake> configuration;
        private FakeConfiguration<TFake>.ReturnValueConfiguration<TReturn> returnValueConfiguration;

        internal RhinoCompatibleConfiguration(FakeConfiguration<TFake> configuration)
        {
            this.configuration = configuration;
            this.returnValueConfiguration = new FakeConfiguration<TFake>.ReturnValueConfiguration<TReturn>() { ParentConfiguration = this.configuration };
        }

        public RhinoCompatibleConfiguration<TFake, TReturn> Return(TReturn returnValue)
        {
            this.returnValueConfiguration.Returns(returnValue);
            return this;
        }

        public RhinoCompatibleConfiguration<TFake, TReturn> Throw(Exception exception)
        {
            this.configuration.Throws(exception);
            return this;
        }

        public RepeatConfig Repeat
        {
            get
            {
                return new RepeatConfig() { ParentConfiguration = this };
            }
        }

        public RhinoCompatibleConfiguration<TFake, TReturn> IgnoreArguments()
        {
            this.configuration.RuleBeingBuilt.IgnoreArguments();
            return this;
        }

        public class RepeatConfig
        {
            internal RhinoCompatibleConfiguration<TFake, TReturn> ParentConfiguration;

            public RhinoCompatibleConfiguration<TFake, TReturn> Once()
            {
                this.ParentConfiguration.configuration.Once();
                return this.ParentConfiguration;
            }

            public RhinoCompatibleConfiguration<TFake, TReturn> Twice()
            {
                this.ParentConfiguration.configuration.Twice();
                return this.ParentConfiguration;
            }

            public RhinoCompatibleConfiguration<TFake, TReturn> Never()
            {
                throw new NotSupportedException();
            }
        }
    }
}
